<?php
	class Core_SSH_TripleDes{
	  private $enc_m;
	  
	  private $dec_m;
	
	  private $enc_ctxs;
	  
	  private $dec_ctxs;
	
	  public function __construct(){
	    $this->enc_m = mcrypt_module_open('des', '', 'cbc', '');
	    
	    $this->dec_m = mcrypt_module_open('des', '', 'cbc', '');
	  }
	
	  public function __destruct(){
	    mcrypt_module_close($this->enc_m);
	    
	    mcrypt_module_close($this->dec_m);
	  }
	
	  public function setKeys($key){
	    $this->enc_ctxs[0] = new Core_SSH_TripleDesCtx(substr($key, 0, 8), integer2dword(0), integer2dword(0));
	    
	    $this->enc_ctxs[1] = new Core_SSH_TripleDesCtx(substr($key, 8, 8), integer2dword(0), integer2dword(0));
	    
	    $this->enc_ctxs[2] = new Core_SSH_TripleDesCtx(substr($key, 16, 8), integer2dword(0), integer2dword(0));
	
	    $this->dec_ctxs[0] = new Core_SSH_TripleDesCtx(substr($key, 0, 8), integer2dword(0), integer2dword(0));
	    
	    $this->dec_ctxs[1] = new Core_SSH_TripleDesCtx(substr($key, 8, 8), integer2dword(0), integer2dword(0));
	    
	    $this->dec_ctxs[2] = new Core_SSH_TripleDesCtx(substr($key, 16, 8), integer2dword(0), integer2dword(0));
	  }
	
	  private function desEncipher(&$data, &$ctx){
	    mcrypt_generic_init($this->enc_m, $ctx->key, str_repeat(chr(0), 8));
	    
	    $enc = mcrypt_generic($this->enc_m,$data);
	    
	    mcrypt_generic_deinit($this->enc_m);
	    
	    return $enc;
	  }
	
	  private function desDecipher ($data, &$ctx){
	    mcrypt_generic_init($this->dec_m, $ctx->key, str_repeat(chr(0), 8));
	    
	    $dec = mdecrypt_generic($this->dec_m, $data);
	    
	    mcrypt_generic_deinit($this->dec_m);
	    
	    return $dec;
	  }
	
	  private function desCbcEncrypt(&$data, &$ctx){
	    if((strlen($data) & 7) != 0){
	      throw new DESException('TAMANHO DO BLOCO DEVE SER MÚLTIPLO DE 8');
	    }
	
	    $iv0 = $ctx->iv0;
	    
	    $iv1 = $ctx->iv1;
	
	    $result = "";
	
	    for ($i = 0; $i < strlen($data); $i += 8){
	      $iv0 = Core_SSH_Functions::ulXor($iv0, substr($data, $i, 4));
	      
	      $iv1 = Core_SSH_Functions::ulXor($iv1, substr($data, $i + 4, 4));
	      
	      $iv = $iv0.$iv1;
	      
	      $out = $this->des_encipher($iv, $ctx);
	      
	      $iv0 = substr($out, 0, 4);
	      
	      $iv1 = substr($out, 4, 4);
	
	      $result.= $out;
	    }
	
	    $ctx->iv0 = $iv0;
	    
	    $ctx->iv1 = $iv1;
	    
	    $data = $result;
	  }
	
	  private function desCbcDecrypt(&$data, &$ctx){
	    if ((strlen($data) & 7) != 0){
	      throw new DESException('TAMANHO DO BLOCO DEVE SER MÚLTIPLO DE 8');
	    }
	
	    $iv0 = $ctx->iv0;
	    
	    $iv1 = $ctx->iv1;
	
	    $result = "";
	
	    for($i = 0; $i < strlen($data); $i += 8){
	      $l = substr($data, $i, 4);
	      
	      $r = substr($data, $i + 4, 4);
	      
	      $out = $this->des_decipher($l.$r,$ctx);
	      
	      $iv0 = Core_SSH_Functions::ulXor($iv0, substr($out, 0, 4));
	      $iv1 = Core_SSH_Functions::ulXor($iv1, substr($out, 4, 4));
	      
	      $result.= $iv0 . $iv1;
	      
	      $iv0 = $l;
	      
	      $iv1 = $r;
	    }
	    
	    $ctx->iv0 = $iv0;
	    
	    $ctx->iv1 = $iv1;
	    
	    $data = $result;
	  }
	
	  private function desTripleCbcEncrypt(&$data, &$ctxs){
	    $this->desCbcEncrypt($data, $ctxs[0]);
	    
	    $this->desCbcDecrypt($data, $ctxs[1]);
	    
	    $this->desCbcEncrypt($data, $ctxs[2]);
	  }
	
	  private function desTripleCbcDecrypt(&$data, &$ctxs){
	    $this->desCbcDecrypt($data, $ctxs[2]);
	    
	    $this->desCbcEncrypt($data, $ctxs[1]);
	    
	    $this->desCbcDecrypt($data, $ctxs[0]);
	  }
	
	  public function encrypt(&$data){
	    $this->desTripleCbcEncrypt($data, $this->enc_ctxs);
	  }
	
	  public function decrypt(&$data){
	    $this->desTripleCbcDecrypt($data, $this->dec_ctxs);
	  }
	}